package p2pcdnsim.network;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;

import p2pcdnsim.exception.NodeUnexistentException;

public class ShortestPathRouterTableBuilder {
	
	private NetworkTopology topology;
	private Hashtable<NetworkAddress, Integer> positions;
	private Link[][] links;
	private int[][] distances;

	public ShortestPathRouterTableBuilder(NetworkTopology t) {
		this.topology = t;
		
		RouterNode[] nodes = t.getNodes();
		positions = new Hashtable<NetworkAddress, Integer>();
		for(int i=0;i<nodes.length;i++)
			positions.put(nodes[i].getNetworkLayer().getDefaultAddress(), i);
		links = new Link[nodes.length][nodes.length];
		distances = new int[nodes.length][nodes.length];
		
	}
	
	
	public RouterTable getRouterTable(int nodeId) throws NodeUnexistentException {
		RouterNode node = topology.getNode(nodeId);

		
		HashMap<GenericAddress, NodeDistance> path = new HashMap<GenericAddress, NodeDistance>();
		NodeDistance myself = new NodeDistance();
		myself.link = null;
		myself.distance = 0;
		myself.address = (GenericAddress)node.getNetworkLayer().getDefaultAddress();
		path.put(myself.address, myself);
		
		Iterator<Link> it = node.getLinks().iterator();
		while(it.hasNext()) {
			Link link = (Link)it.next();
			walk(path, (RouterNode)link.getNodeTo(), link, 1);
		}
		
		int nodePosition = positions.get(node.getNetworkLayer().getDefaultAddress());
		RouterTable t = new RouterTable(node,nodePosition,links,distances,positions);
		Iterator<NodeDistance> itn = path.values().iterator();
		//System.out.println("Router table node "+node.getNodeId());
		while(itn.hasNext()) {
			NodeDistance nd = itn.next();
			if(!node.getNetworkLayer().getDefaultAddress().equals(nd.address))
				t.addEntry(nd.address, nd.link, nd.distance);
			//System.out.println("\tto node "+nd.address+": use link "+nd.link+", distance "+nd.distance);
		}
		
		return t;
	}
	


	private void walk(HashMap<GenericAddress, NodeDistance> path, RouterNode node, Link linkId, int distance) {
		GenericAddress address = (GenericAddress)node.getNetworkLayer().getDefaultAddress();
		
		NodeDistance nd = path.get(address);
		if(nd == null) {
			nd = new NodeDistance();
			nd.link = linkId;
			nd.distance = distance;
			nd.address = address;
			path.put(address, nd);
		} else {
			if(nd.distance > distance) {
				nd.link = linkId;
				nd.distance = distance;
			} else return;
		}
		Iterator<Link> it = node.getLinks().iterator();
		while(it.hasNext()) {
			Link link = (Link)it.next();
			walk(path, (RouterNode)link.getNodeTo(), linkId, distance + 1);
		}
	}



	private class NodeDistance{
		Link link;
		int distance;
		GenericAddress address;
	}
}
